package orm

import (
	"context"
	"time"

	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/gorm/schema"
	"gorm.io/gorm/utils"

	"mini-admin/pkg/log"
)

// Config mysql config.
type Config struct {
	DSN            string
	Active         int
	Idle           int
	IdleTimeout    time.Duration
	NamingStrategy schema.NamingStrategy
}

// Logger 自定义logger
type Logger struct {
	LogLevel logger.LogLevel
}

// LogMode 日志级别
func (l *Logger) LogMode(level logger.LogLevel) logger.Interface {
	newLogger := *l
	newLogger.LogLevel = level
	return &newLogger
}

// Info Info
func (l Logger) Info(ctx context.Context, msg string, data ...interface{}) {
	_logger := log.FromContext(ctx)
	if l.LogLevel >= logger.Info {
		_logger.Infof("%s\n"+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

// Warn Warn
func (l Logger) Warn(ctx context.Context, msg string, data ...interface{}) {
	_logger := log.FromContext(ctx)
	if l.LogLevel >= logger.Warn {
		_logger.Warnf("%s"+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

// Error Error
func (l Logger) Error(ctx context.Context, msg string, data ...interface{}) {
	_logger := log.FromContext(ctx)
	if l.LogLevel >= logger.Error {
		_logger.Errorf("%s"+msg, append([]interface{}{utils.FileWithLineNum()}, data...)...)
	}
}

// Trace 追踪日志
func (l Logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
	_logger := log.FromContext(ctx)
	if l.LogLevel > 0 {
		elapsed := time.Since(begin)
		switch {
		case err != nil && l.LogLevel >= logger.Error:
			sql, rows := fc()
			_logger.Infof("%s %s\n[%v] [rows:%d] %s", utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql)
		case elapsed > 100*time.Millisecond && l.LogLevel >= logger.Warn:
			sql, rows := fc()
			_logger.Infof("%s\n[%v] [rows:%d] %s", utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
		case l.LogLevel >= logger.Info:
			sql, rows := fc()
			_logger.Infof("%s\n[%v] [rows:%d] %s", utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql)
		}
	}
}

// NewPostgreSQL new PostgreSQL
func NewPostgreSQL(c *Config) (*gorm.DB, error) {
	db, err := gorm.Open(postgres.Open(c.DSN), &gorm.Config{Logger: &Logger{LogLevel: logger.Info}, NamingStrategy: c.NamingStrategy})
	if err != nil {
		return nil, err
	}
	sqlDB, err := db.DB()
	if err != nil {
		return nil, err
	}
	sqlDB.SetMaxIdleConns(c.Idle)
	sqlDB.SetMaxOpenConns(c.Active)
	sqlDB.SetConnMaxLifetime(c.IdleTimeout)
	return db, err
}
